
#include "USBManager.h"
#include "common.h"

#if defined CONFIG_ESP_SYSTEM_MEMPROT_FEATURE || defined FORCE_TEMPLATED_NOPS || defined CONFIG_IDF_TARGET_ESP32S3
#pragma message "memory protection features disabled, templated asm nop() will be used"
#include "nops.hpp"
#endif

USBClientConnector USBManager::s_usbConnectors[2];

void USBManager::init(int DP0, int DM0, int DP1, int DM1, int DP2, int DM2, int DP3, int DM3) {

    s_usbConnectors[0].usbNum = 0;
    s_usbConnectors[1].usbNum = 1;
    _SUSB.setOnDetect(UsbOnDetect);
    _SUSB.setOnEject(UsbOnEject);

    _SUSB.setOnError(UsbOnError);
    _SUSB.setOnRecvData(UsbOnRecvData);

    _SUSB.init(DP0, DM0, DP1, DM1, DP2, DM2, DP3, DM3);
}

USBClientConnector *USBManager::getUSBClientConnector(uint8_t usbNum) {
    return &s_usbConnectors[usbNum];
}

// 检测到插入设备
void USBManager::UsbOnDetect(uint8_t usbNum, sDevDesc *devDesc, size_t len) {
    s_usbConnectors[usbNum]._status = 1;

    Serial.printf("USB Detected #%d\n", usbNum);

    _USBManager.OnDevDesc(usbNum, (uint8_t *)devDesc, len);

    // 获取设备名称

    // 获取配置描述符
    uint8_t tryTime = 30;
    int cfgDescLen = 0;
    uint8_t *pDdata = nullptr;
    uint8_t data[8];
    while (tryTime--) {
        memcpy(data, SetupGetCfgDescr, 8);
        data[6] = 0x08;
        cfgDescLen = 0;
        pDdata = _SUSB.usbSetupIn(usbNum, (uint8_t *)SetupGetCfgDescr, 0x08, &cfgDescLen);

        if (cfgDescLen) {
            // 重新获取配置描述符
            memcpy(data, SetupGetCfgDescr, 8);
            data[6] = pDdata[2];
            delete[] pDdata;

            vTaskDelay(2);
            pDdata = _SUSB.usbSetupIn(usbNum, data, data[6], &cfgDescLen);
        } else {
            continue;
        }

        if (cfgDescLen != data[6]) {
            cfgDescLen = 0;
            // 读取失败
            delete[] pDdata;
            continue;
        }
        // 读到
        break;
    }

    if (!cfgDescLen) {
        return;
    }

    int cfgNum = 0;
    int sIntfNum = -1;
    int hidNum = -1;
    int epCount = 0;
    int pos = 0;

    // 枚举设备描述符
    while (pos < cfgDescLen - 2) {
        uint8_t len = pDdata[pos];
        uint8_t type = pDdata[pos + 1];
        if (len == 0) {
            pos = cfgDescLen;
        }
        if (pos + len <= cfgDescLen) {
            // printf("\n");
            if (type == 0x2) {
                // Config Descriptor
                cfgNum++;
                _USBManager.OnConfigDescCB(usbNum, cfgNum, &pDdata[pos], len);

            } else if (type == 0x4) {
                // Interface Descriptor
                sIntfNum++;
                _USBManager.OnIFaceDesc(usbNum, cfgNum, sIntfNum, &pDdata[pos], len);

            } else if (type == 0x21) {
                // iInterface (HID Device Descriptor)
                hidNum++;
                _USBManager.OnHidDevDesc(usbNum, cfgNum, sIntfNum, hidNum, &pDdata[pos], len);
            } else if (type == 0x05) {
                // EndPoint Descriptor
                epCount++;
                _USBManager.OnEndPointDesc(usbNum, cfgNum, sIntfNum, epCount, &pDdata[pos], len);
            }
        }
        pos += len;
    }
    delete[] pDdata;

    // 完成设备描述符枚举后
    Serial.printf("tryTime:%d\n", 30 - tryTime);
    Serial.printf("cfg:%d, iface:%d\n", s_usbConnectors[usbNum].cfgNum, s_usbConnectors[usbNum].interfaceNum);

    // SET_CONFIGURATION——选择配置
    memcpy(data, SetupSetConfig, 8);
    data[2] = s_usbConnectors[usbNum].cfgNum;
    _SUSB.usbSetupIn(usbNum, data, 0, 0);

    // SET_INFERTACE    ——选择接口
    memcpy(data, SetupSetInterface, 8);
    data[4] = s_usbConnectors[usbNum].interfaceNum;
    _SUSB.usbSetupIn(usbNum, data, 0, 0);

    // 设置HID设备的上传速率——数据有改变才上传
    memcpy(data, SetupHIDSetIdle, 8);
    _SUSB.usbSetupIn(usbNum, data, 0, 0);

    // 指定监听端点
    _SUSB.setEndPoint(usbNum, s_usbConnectors[usbNum].epMin, s_usbConnectors[usbNum].epMax, s_usbConnectors[usbNum].interval);
    bool bFlag;
    _USBManager.onConnectCallback(usbNum, bFlag);
    if (bFlag) {
        s_usbConnectors[usbNum]._status = 2;
    }
}

void USBManager::UsbOnEject(uint8_t usbNum) {

    Serial.printf("USBManager::UsbOnEject  #%d\n", usbNum);
    s_usbConnectors[usbNum]._status = 0;

    _USBManager.onDisconnectCallback(usbNum);
}

// 收到设备描述符
void USBManager::OnDevDesc(uint8_t usbNum, uint8_t *data, size_t data_len) {
    sDevDesc *device = (sDevDesc *)data;
    printf("New device detected on USB#%d\n", usbNum);
    printf("desc.bcdUSB             = 0x%04x\n", device->bcdUSB);
    printf("desc.bDeviceClass       = 0x%02x\n", device->bDeviceClass);
    printf("desc.bDeviceSubClass    = 0x%02x\n", device->bDeviceSubClass);
    printf("desc.bDeviceProtocol    = 0x%02x\n", device->bDeviceProtocol);
    printf("desc.bMaxPacketSize0    = 0x%02x\n", device->bMaxPacketSize0);
    printf("desc.idVendor           = 0x%04x\n", device->idVendor);
    printf("desc.idProduct          = 0x%04x\n", device->idProduct);
    printf("desc.bcdDevice          = 0x%04x\n", device->bcdDevice);
    printf("desc.iManufacturer      = 0x%02x\n", device->iManufacturer);
    printf("desc.iSerialNumber      = 0x%02x\n", device->iSerialNumber);
    printf("desc.bNumConfigurations = 0x%02x\n", device->bNumConfigurations);

    // USB设备标识
    memcpy(s_usbConnectors[usbNum]._DeviceID, &(device->idVendor), 4);
}

// 收到配置描述符
void USBManager::OnConfigDescCB(uint8_t usbNum, int cfgNum, uint8_t *pData, size_t len) {
    sCfgDesc *lcfg = (sCfgDesc *)pData;
    printf("Config Descriptor #%d\n", cfgNum);
    printf("  cfg.wLength         = 0x%02x\n", lcfg->wLength);
    printf("  cfg.bNumIntf        = 0x%02x\n", lcfg->bNumIntf);
    printf("  cfg.bCV             = 0x%02x\n", lcfg->bCV);
    printf("  cfg.bMaxPower       = %d\n", lcfg->bMaxPower);
}

// 收到接口描述符
void USBManager::OnIFaceDesc(uint8_t usbNum, int cfgNum, int sIntfNum, uint8_t *pData, size_t len) {
    sIntfDesc *sIntf = (sIntfDesc *)pData;
    printf("    Interface Descriptor usb:%d #%d_%d\n", usbNum, cfgNum, sIntfNum);
    printf("      sIntf.bLength     = 0x%02x\n", sIntf->bLength);
    printf("      sIntf.bType       = 0x%02x\n", sIntf->bType);
    printf("      sIntf.iNum        = 0x%02x\n", sIntf->iNum);
    printf("      sIntf.iAltString  = 0x%02x\n", sIntf->iAltString);
    printf("      sIntf.bEndPoints  = 0x%02x\n", sIntf->bEndPoints);
    printf("      sIntf.iClass      = 0x%02x\n", sIntf->iClass);
    printf("      sIntf.iSub        = 0x%02x\n", sIntf->iSub);
    printf("      sIntf.iProto      = 0x%02x\n", sIntf->iProto);
    printf("      sIntf.iIndex      = 0x%02x\n", sIntf->iIndex);

    if (sIntf->iProto == 0x01 || sIntf->iProto == 0x03) {
        s_usbConnectors[usbNum]._type = 1;
        s_usbConnectors[usbNum].cfgNum = cfgNum;
        s_usbConnectors[usbNum].interfaceNum = sIntf->iNum;
    } else if (sIntf->iProto == 0x02) {
        s_usbConnectors[usbNum]._type = 2;
        s_usbConnectors[usbNum].cfgNum = cfgNum;
        s_usbConnectors[usbNum].interfaceNum = sIntf->iNum;
    } else {
        return;
    }
}

// USB: 枚举HID类描述符
void USBManager::OnHidDevDesc(uint8_t usbNum, int cfgNum, int sIntfNum, int hidNum, uint8_t *vhid, size_t len) {
    HIDDescriptor *hid = (HIDDescriptor *)vhid;
    printf("        HID Device Descriptor #%d\n", hidNum);
    printf("          hid.bLength               = 0x%02x\n", hid->bLength);
    printf("          hid.bDescriptorType       = 0x%02x\n", hid->bDescriptorType);
    printf("          hid.bcdHID                = 0x%02x\n", hid->bcdHID);
    printf("          hid.bCountryCode          = 0x%02x\n", hid->bCountryCode);
    printf("          hid.bNumDescriptors       = 0x%02x\n", hid->bNumDescriptors);
    printf("          hid.bReportDescriptorType = 0x%02x\n", hid->bReportDescriptorType);
    printf("          hid.wItemLengthL          = 0x%04x\n", hid->wItemLengthL);
    printf("          hid.wItemLengthH          = 0x%04x\n", hid->wItemLengthH);

    if (s_usbConnectors[usbNum].cfgNum == cfgNum && s_usbConnectors[usbNum].interfaceNum == sIntfNum) {

        if (hid->bCountryCode == 0x21) {
            s_usbConnectors[usbNum]._keyboard_mode = 1; // 日语键盘
        } else {
            s_usbConnectors[usbNum]._keyboard_mode = 0; // 美式键盘
        }

        // 每个HID类描述符至少包含一个 bReportDescriptorType=0x22的报表描述符
        uint8_t *ptr = &hid->bReportDescriptorType;
        for (int i = 0; i < hid->bNumDescriptors; i++) {
            if (*ptr == 0x22) {
                ptr++; // 指向 wItemLengthL
                s_usbConnectors[usbNum].hidReportDescLen = *ptr;
                break;
            } else {
                ptr += 3;
            }
        }
    }
}

void USBManager::OnEndPointDesc(uint8_t usbNum, int cfgNum, int sIntfNum, int epdNUm, uint8_t *data, size_t len) {
    sEPDesc *epd = (sEPDesc *)data;
    printf("        EndPoint Descriptor #%d\n", epdNUm);
    printf("          epd.bLength       = 0x%02x\n", epd->bLength);
    printf("          epd.bType         = 0x%02x\n", epd->bType);
    printf("          epd.bEPAdd        = 0x%02x\n", epd->bEPAdd);
    printf("          epd.bAttr         = 0x%02x\n", epd->bAttr);
    printf("          epd.wPayLoad      = 0x%02x\n", epd->wPayLoad);
    printf("          epd.bInterval     = 0x%02x\n", epd->bInterval);

    if (s_usbConnectors[usbNum].cfgNum == cfgNum && s_usbConnectors[usbNum].interfaceNum == sIntfNum) {
        if (s_usbConnectors[usbNum].epMin == 0) {
            s_usbConnectors[usbNum].epMin = epdNUm;
        }
        s_usbConnectors[usbNum].epMax = epdNUm;
        s_usbConnectors[usbNum].interval = epd->bInterval;
    }
}

void USBManager::UsbOnError(uint8_t usbNum, int errorCode) {
    printf("ERROR: usb:%s  code:%d\n", usbNum, errorCode);
}

void USBManager::UsbOnRecvData(uint8_t usbNum, uint8_t *data, size_t data_len) {
    s_usbConnectors[usbNum].onRecvData(data, data_len);
}

void USBManager::setOnConnect(OnConnect_t cd) {
    onConnectCallback = cd;
}

void USBManager::setOnDisonnect(OnDisonnect_t cd) {
    onDisconnectCallback = cd;
}

USBManager _USBManager;
